package server

import (
	"cgs-server/helper"
	"cgs-server/server/model"
	"context"
	"fmt"
	"github.com/dimfeld/httptreemux"
	"github.com/gin-gonic/gin"
	"github.com/urfave/negroni"
	"log"
	"mime"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"strings"
)

var (
	// mux maps request url to handler.
	mux *httptreemux.ContextMux
	// apiAuthorities means the authority that api requires.
	apiAuthorities  = map[string]Authority{}
	apiAuthorities2 = map[string]*model.ApiAuthority{}
	g               *gin.Engine
)

func init() {
	mux = httptreemux.NewContextMux()
	//mux.OptionsHandler = corsHandler
}

// Start start the web server.
//
// Negroni is an idiomatic HTTP Middleware for Golang. `negroni.Classic` add three
// middleware: Recovery, Logger, Static. We add two: CrossOriginHandler, GZipHandler.
//
// Then, we use `httptreemux` to map route to the handler.
func Start() {
	log.Printf("starting cgseditor server on port %v", Config.Server.Port)

	// register custom mime-type
	mime.AddExtensionType(".js", "application/javascript; charset=UTF-8")

	recovery := negroni.NewRecovery()
	logger := negroni.NewLogger()
	//static := negroni.NewStatic(http.Dir("public"))
	static := negroni.NewStatic(http.Dir(Config.Path.PublicDir))

	handler := negroni.New(recovery, logger)
	handler.Use(negroni.HandlerFunc(OptionsHandler))
	//handler.Use(negroni.HandlerFunc(CrossOriginMiddleware))
	handler.Use(static)
	handler.Use(negroni.HandlerFunc(GZipMiddleware))
	handler.Use(negroni.HandlerFunc(AccessTokenMiddleware))
	handler.Use(negroni.HandlerFunc(ValidateTokenMiddleware2))
	handler.Use(negroni.HandlerFunc(SdkMiddleware))
	handler.UseHandler(mux)

	srv := http.Server{Addr: Config.Server.Port, Handler: handler}
	idleConnsClosed := make(chan struct{})
	go func() {
		sigint := make(chan os.Signal, 1)
		signal.Notify(sigint, os.Interrupt)
		<-sigint

		// We received an interrupt signal, shut down.
		if err := srv.Shutdown(context.Background()); err != nil {
			// Error from closing listeners, or context timeout.
			log.Printf("HTTP server Shutdown: %v", err)
		}
		close(idleConnsClosed)
	}()

	go StartMDDataSync()

	// websocket
	go WebsocketHub.Run()

	pprofMux := http.DefaultServeMux
	http.DefaultServeMux = http.NewServeMux()
	go func() {
		log.Fatal(http.ListenAndServe("localhost:2021", pprofMux))
	}()

	if err := srv.ListenAndServe(); err != http.ErrServerClosed {
		// Error starting or closing listener:
		log.Printf("HTTP server ListenAndServe: %v", err)
	}

	<-idleConnsClosed
}

// Handle allows handling HTTP requests via an http.HandlerFunc. Authority means the required
// authority to request this api. No authority is needed when authority is nil.
func Handle(method, path string, handler http.HandlerFunc, authority Authority) {
	if _, ok := apiAuthorities[path]; ok {
		panic(fmt.Errorf("path (%v) has already been handled", path))
	}
	apiAuthorities[path] = authority
	mux.UsingContext().Handle(method, path, handler)
}

func Handle2(method, path string, handler http.HandlerFunc, desc string, auth string) {
	if _, ok := apiAuthorities2[path]; ok {
		panic(fmt.Errorf("path (%v) has already been handled", path))
	}
	apiAuthorities2[path] = &model.ApiAuthority{
		ApiPath:       path,
		Group:         path[:strings.LastIndex(path, "/")],
		Description:   desc,
		RequestMethod: method,
		Type:          "API",
		Auth:          auth,
		Handler:       handler,
	}
	//mux.UsingContext().Handle(method, path, handler)
}

// InitRoute 初始化路由
func InitRoute() {
	if g == nil {
		return
	}
	for _, a := range apiAuthorities2 {
		g.Handle(a.RequestMethod, a.ApiPath, getGinHandler(a.Handler))
	}
}

func getGinHandler(f http.HandlerFunc) gin.HandlerFunc {
	return func(c *gin.Context) {
		f(c.Writer, c.Request)
	}
}

// HandleGet
func Get(path string, handler http.HandlerFunc, desc string, auth Authority) {
	Handle2(http.MethodGet, path, handler, desc, string(auth))
}

/*func GetWithoutAuth(path string, handler http.HandlerFunc, desc string, auth Authority) {
	Handle2(http.MethodGet, path, handler, desc, string(auth))
}*/

// HandlePost
func Post(path string, handler http.HandlerFunc, desc string, auth Authority) {
	Handle2(http.MethodPost, path, handler, desc, string(auth))
}

/*func PostWithoutAuth(path string, handler http.HandlerFunc, desc string, auth Authority) {
	Handle2(http.MethodPost, path, handler, desc, string(auth))
}*/

func OptionsHandler(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	if r.Method == "OPTIONS" {
		w.WriteHeader(http.StatusOK)
		return
	} else {
		next(w, r)
	}

}

// corsHandler handles OPTIONS request when cross origin.
func corsHandler(w http.ResponseWriter, r *http.Request, params map[string]string) {
	helper.EnableCrossDomain(w, r)
}

func GetApiAuthority(path string) (*model.ApiAuthority, bool) {
	authority, ok := apiAuthorities2[path]
	return authority, ok
}

func SetEngine(ge *gin.Engine) {
	g = ge
}
